﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace InterfaceFiles
{
  public class JulianDate
  {
    int rawJulianDate;

    public JulianDate(int aJulianDate)
    {
      rawJulianDate = aJulianDate;
    }

    public int Year
    {
      get
      {
    	  return rawJulianDate / 1000;
      }
      set
      {
        int yearJulianDate = value * 1000;
        int dayOfYear = rawJulianDate - this.Year * 1000;
        rawJulianDate = dayOfYear + yearJulianDate;
      }
    }

    public int Day
    {
      get
      {
        return rawJulianDate - (rawJulianDate / 1000) * 1000;
      }
      set
      {
        int yearJulianDate = rawJulianDate / 1000 * 1000;
      	rawJulianDate = yearJulianDate + value;
      }
    }

    private static int DaysOfYearPassed(int month, bool isLeapYear)
    {
      int LYDay = isLeapYear ? 1 : 0;
      int daysOfYearPassed = 0;

      if (month > 1)
        daysOfYearPassed += 31;
      if (month > 2)
        daysOfYearPassed += 28 + LYDay;
      if (month > 3)
        daysOfYearPassed += 31;
      if (month > 4)
        daysOfYearPassed += 30;
      if (month > 5)
        daysOfYearPassed += 31;
      if (month > 6)
        daysOfYearPassed += 30;
      if (month > 7)
        daysOfYearPassed += 31;
      if (month > 8)
        daysOfYearPassed += 31;
      if (month > 9)
        daysOfYearPassed += 30;
      if (month > 10)
        daysOfYearPassed += 31;
      if (month > 11)
        daysOfYearPassed += 30;

      return daysOfYearPassed;
    }

    public int DayOfMonth
    {
      get
      {
        int LYDay;
	      if (DateTime.IsLeapYear(Year))
		      LYDay = 1;
	      else
		      LYDay = 0;

        int day = Day;
        int result = 0;
        if (day <= 31)  // Jan
          result = day;
        else if ((day - LYDay) <= 59)   // Feb
          result = day - 31;
        else if ((day - LYDay) <= 90)   // Mar
          result = (day - LYDay) - 59;
        else if ((day - LYDay) <= 120)   // Apr
          result = (day - LYDay) - 90;
        else if ((day - LYDay) <= 151)   // May
          result = (day - LYDay) - 120;
        else if ((day - LYDay) <= 181)   // Jun
          result = (day - LYDay) - 151;
        else if ((day - LYDay) <= 212)   // Jul
          result = (day - LYDay) - 181;
        else if ((day - LYDay) <= 243)   // Aug
          result = (day - LYDay) - 212;
        else if ((day - LYDay) <= 273)   // Sep
          result = (day - LYDay) - 243;
        else if ((day - LYDay) <= 304)   // Oct
          result = (day - LYDay) - 273;
        else if ((day - LYDay) <= 334)   // Nov
          result = (day - LYDay) - 304;
        else  // Dec
          result = (day - LYDay) - 334;

        return result;
      }
      set
      {
        int month = Month;
        int[] month30Days = { 4, 6, 9, 11 };
        int[] month31Days = { 1, 3, 5, 7, 8, 10, 12 };
        int LYDay = DateTime.IsLeapYear(Year) ? 1 : 0;

        if (month30Days.Contains(month))
        {
          if ((value > 30) || (value < 1))
            throw new ArgumentOutOfRangeException("DayOfMonth", value, 
              string.Format("For month {0}, valid range is 1-{1}", month, 
                30));
        }
        else if ((month31Days.Contains(month)))
        {
          if ((value > 31) || (value < 1))
            throw new ArgumentOutOfRangeException("DayOfMonth", value, 
              string.Format("For month {0}, valid range is 1-{1}", month, 
                31));
        }
        else
        {

          int febLength = 28 + LYDay;
          if (value > febLength)
            throw new ArgumentOutOfRangeException("DayOfMonth", value, 
              string.Format("For month {0}, valid range is 1-{1}", month, 
                febLength));
        }

        int daysOfYearPassed = 0;
        daysOfYearPassed = DaysOfYearPassed(month, DateTime.IsLeapYear(Year));

        rawJulianDate = month * 1000 + daysOfYearPassed + value;
      }
    }

    public int Month
    {
      get
      {
        int LYDay;
        if (DateTime.IsLeapYear(Year))
          LYDay = 1;
        else
          LYDay = 0;

        int day = Day;
        int result = 0;
        if (day <= 31)  // Jan
          result = 1;
        else if ((day - LYDay) <= 59)   // Feb
          result = 2;
        else if ((day - LYDay) <= 90)   // Mar
          result = 3;
        else if ((day - LYDay) <= 120)   // Apr
          result = 4;
        else if ((day - LYDay) <= 151)   // May
          result = 5;
        else if ((day - LYDay) <= 181)   // Jun
          result = 6;
        else if ((day - LYDay) <= 212)   // Jul
          result = 7;
        else if ((day - LYDay) <= 243)   // Aug
          result = 8;
        else if ((day - LYDay) <= 273)   // Sep
          result = 9;
        else if ((day - LYDay) <= 304)   // Oct
          result = 10;
        else if ((day - LYDay) <= 334)   // Nov
          result = 11;
        else  // Dec
          result = 12;

        return result;
      }
      set
      {
        if ((value < 1) || (value > 12))
          throw new ArgumentOutOfRangeException("Month", value,
              "Valid range is 1-12");

        int dayOfMonth = DayOfMonth;
        int yearJulianDate = Year * 1000;
        int daysOfYearPassed = 
          DaysOfYearPassed(value, DateTime.IsLeapYear(Year));

        rawJulianDate = yearJulianDate + daysOfYearPassed + dayOfMonth;
      }
    }

    public static implicit operator DateTime(JulianDate julianDate)
    {
      int year = julianDate.Year;
      int month = julianDate.Month;
      int day = julianDate.DayOfMonth;

      return new DateTime(year, month, day);
    }

    public static explicit operator JulianDate(DateTime dateTime)
    {
      int year = dateTime.Year;
      int month = dateTime.Month;
      int day = dateTime.Day;
      int julianDate = year * 1000 + 
        DaysOfYearPassed(month, DateTime.IsLeapYear(year)) + day;

      return new JulianDate(julianDate);
    }

    public int Raw
    {
      get
      {
        return rawJulianDate;
      }
    }

    public static int YearPart(int julianDate)
    {
      return julianDate / 1000;
    }

    public static int DayPart(int julianDate)
    {
      return julianDate - julianDate / 1000 * 1000;
    }

    public static explicit operator JulianDate(int julianDate)
    {
      int year = YearPart(julianDate);
      int day = DayPart(julianDate);
      bool isLeapYear = DateTime.IsLeapYear(year);

      if (day > 365 + (isLeapYear ? 1 : 0))
        throw new ArgumentOutOfRangeException("Day", day, 
          string.Format("{0} has a daypart {2} > {1}", 
          julianDate, 365 + (isLeapYear ? 1 : 0), day));

      return new JulianDate(julianDate);
    }
  }
}
